Add GtkLayoutChild
authorEmmanuele Bassi <ebassi@gnome.org>
Wed, 19 Dec 2018 16:01:17 +0000 (16:01 +0000)
committerEmmanuele Bassi <ebassi@gnome.org>
Tue, 26 Mar 2019 00:11:27 +0000 (00:11 +0000)
Layout managers needs a way to store properties that control the layout
policy of a widget; typically, we used to store these in GtkContainer's
child properties, but since GtkLayoutManager is decoupled from the
actual container widget, we need a separate storage. Additionally, child
properties have their own downsides, like requiring a separate, global
GParamSpecPool storage, and additional lookup API.

GtkLayoutChild is a simple GObject class, which means you can introspect
and document it as you would any other type.

docs/reference/gtk/gtk4-sections.txt
gtk/gtk.h
gtk/gtklayoutchild.c [new file with mode: 0644]
gtk/gtklayoutchild.h [new file with mode: 0644]
gtk/gtklayoutmanager.c
gtk/gtklayoutmanager.h
gtk/meson.build

index 1179eeaa2fba6025e989ae4a5f3c7410d694c0b2..4d8f1eeacb5c67f77b85770b621e3c3caecc807d 100644 (file)
@@ -7164,9 +7164,23 @@ gtk_layout_manager_measure
 gtk_layout_manager_allocate
 gtk_layout_manager_get_request_mode
 gtk_layout_manager_get_widget
+gtk_layout_manager_get_layout_child
 gtk_layout_manager_layout_changed
 
 <SUBSECTION Standard>
 GTK_TYPE_LAYOUT_MANAGER
 gtk_layout_manager_get_type
 </SECTION>
+
+<SECTION>
+<FILE>gtklayoutchild</FILE>
+GtkLayoutChild
+GtkLayoutChildClass
+
+gtk_layout_child_get_layout_manager
+gtk_layout_child_get_child_widget
+
+<SUBSECTION Standard>
+GTK_TYPE_LAYOUT_CHILD
+gtk_layout_child_get_type
+</SECTION>
index d6e419a55b8ed12d89d641ddf497b1d1b2be06d0..9bc2e574235070a2f705fd0e61ec25742765ae1b 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
 #include <gtk/gtklabel.h>
 #include <gtk/gtklayout.h>
 #include <gtk/gtklayoutmanager.h>
+#include <gtk/gtklayoutchild.h>
 #include <gtk/gtklevelbar.h>
 #include <gtk/gtklinkbutton.h>
 #include <gtk/gtklistbox.h>
diff --git a/gtk/gtklayoutchild.c b/gtk/gtklayoutchild.c
new file mode 100644 (file)
index 0000000..028e802
--- /dev/null
@@ -0,0 +1,189 @@
+#include "config.h"
+
+#include "gtklayoutchild.h"
+
+#include "gtklayoutmanager.h"
+#include "gtkprivate.h"
+
+/**
+ * SECTION:gtklayoutchild
+ * @Title: GtkLayoutChild
+ * @Short_description: An object containing layout properties
+ *
+ * #GtkLayoutChild is the base class for objects that are meant to hold
+ * layout properties. If a #GtkLayoutManager has per-child properties,
+ * like their packing type, or the horizontal and vertical span, or the
+ * icon name, then the layout manager should use a #GtkLayoutChild
+ * implementation to store those properties.
+ *
+ * A #GtkLayoutChild instance is only ever valid while a widget is part
+ * of a layout.
+ */
+
+typedef struct {
+  GtkLayoutManager *manager;
+  GtkWidget *widget;
+} GtkLayoutChildPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutChild, gtk_layout_child, G_TYPE_OBJECT)
+
+enum {
+  PROP_LAYOUT_MANAGER = 1,
+  PROP_CHILD_WIDGET,
+
+  N_PROPS
+};
+
+static GParamSpec *layout_child_properties[N_PROPS];
+
+static void
+gtk_layout_child_set_property (GObject      *gobject,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  switch (prop_id)
+    {
+    case PROP_LAYOUT_MANAGER:
+      priv->manager = g_value_get_object (value);
+      break;
+
+    case PROP_CHILD_WIDGET:
+      priv->widget = g_value_get_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gtk_layout_child_get_property (GObject      *gobject,
+                               guint         prop_id,
+                               GValue       *value,
+                               GParamSpec   *pspec)
+{
+  GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  switch (prop_id)
+    {
+    case PROP_LAYOUT_MANAGER:
+      g_value_set_object (value, priv->manager);
+      break;
+
+    case PROP_CHILD_WIDGET:
+      g_value_set_object (value, priv->widget);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gtk_layout_child_constructed (GObject *gobject)
+{
+  GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  G_OBJECT_CLASS (gtk_layout_child_parent_class)->constructed (gobject);
+
+  if (priv->manager == NULL)
+    {
+      g_critical ("The layout child of type %s does not have "
+                  "the GtkLayoutChild:layout-manager property set",
+                  G_OBJECT_TYPE_NAME (gobject));
+      return;
+    }
+
+  if (priv->widget == NULL)
+    {
+      g_critical ("The layout child of type %s does not have "
+                  "the GtkLayoutChild:child-widget property set",
+                  G_OBJECT_TYPE_NAME (gobject));
+      return;
+    }
+}
+
+static void
+gtk_layout_child_class_init (GtkLayoutChildClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = gtk_layout_child_set_property;
+  gobject_class->get_property = gtk_layout_child_get_property;
+  gobject_class->constructed = gtk_layout_child_constructed;
+
+  /**
+   * GtkLayoutChild:layout-manager:
+   *
+   * The layout manager that created the #GtkLayoutChild instance.
+   */
+  layout_child_properties[PROP_LAYOUT_MANAGER] =
+    g_param_spec_object ("layout-manager",
+                         "Layout Manager",
+                         "The layout manager that created this object",
+                         GTK_TYPE_LAYOUT_MANAGER,
+                         GTK_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkLayoutChild:child-widget:
+   *
+   * The widget that is associated to the #GtkLayoutChild instance.
+   */
+  layout_child_properties[PROP_CHILD_WIDGET] =
+    g_param_spec_object ("child-widget",
+                         "Child Widget",
+                         "The child widget that is associated to this object",
+                         GTK_TYPE_WIDGET,
+                         GTK_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, layout_child_properties);
+}
+
+static void
+gtk_layout_child_init (GtkLayoutChild *self)
+{
+}
+
+/**
+ * gtk_layout_child_get_layout_manager:
+ * @layout_child: a #GtkLayoutChild
+ *
+ * Retrieves the #GtkLayoutManager instance that created the
+ * given @layout_child.
+ *
+ * Returns: (transfer none): a #GtkLayoutManager
+ */
+GtkLayoutManager *
+gtk_layout_child_get_layout_manager (GtkLayoutChild *layout_child)
+{
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  g_return_val_if_fail (GTK_IS_LAYOUT_CHILD (layout_child), NULL);
+
+  return priv->manager;
+}
+
+/**
+ * gtk_layout_child_get_child_widget:
+ * @layout_child: a #GtkLayoutChild
+ *
+ * Retrieves the #GtkWidget associated to the given @layout_child.
+ *
+ * Returns: (transfer none): a #GtkWidget
+ */
+GtkWidget *
+gtk_layout_child_get_child_widget (GtkLayoutChild *layout_child)
+{
+  GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
+
+  g_return_val_if_fail (GTK_IS_LAYOUT_CHILD (layout_child), NULL);
+
+  return priv->widget;
+}
diff --git a/gtk/gtklayoutchild.h b/gtk/gtklayoutchild.h
new file mode 100644 (file)
index 0000000..3be8fda
--- /dev/null
@@ -0,0 +1,27 @@
+#pragma once
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtktypes.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_LAYOUT_CHILD (gtk_layout_child_get_type())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (GtkLayoutChild, gtk_layout_child, GTK, LAYOUT_CHILD, GObject)
+
+struct _GtkLayoutChildClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+};
+
+GDK_AVAILABLE_IN_ALL
+GtkLayoutManager *      gtk_layout_child_get_layout_manager     (GtkLayoutChild *layout_child);
+GDK_AVAILABLE_IN_ALL
+GtkWidget *             gtk_layout_child_get_child_widget       (GtkLayoutChild *layout_child);
+
+G_END_DECLS
index 7a5f2f08d5e6a6bc58cdac7abdf81cbe340cc81a..02abe0d5c7ec1cd361bbe8d053e9ba249bfd8f6d 100644 (file)
@@ -38,6 +38,7 @@
 #include "config.h"
 
 #include "gtklayoutmanagerprivate.h"
+#include "gtklayoutchild.h"
 #include "gtkwidget.h"
 
 #ifdef G_ENABLE_DEBUG
@@ -57,6 +58,8 @@ typedef struct {
 
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutManager, gtk_layout_manager, G_TYPE_OBJECT)
 
+static GQuark quark_layout_child;
+
 static GtkSizeRequestMode
 gtk_layout_manager_real_get_request_mode (GtkLayoutManager *manager,
                                           GtkWidget        *widget)
@@ -107,6 +110,8 @@ gtk_layout_manager_class_init (GtkLayoutManagerClass *klass)
   klass->get_request_mode = gtk_layout_manager_real_get_request_mode;
   klass->measure = gtk_layout_manager_real_measure;
   klass->allocate = gtk_layout_manager_real_allocate;
+
+  quark_layout_child = g_quark_from_static_string ("-GtkLayoutManager-layout-child");
 }
 
 static void
@@ -278,3 +283,75 @@ gtk_layout_manager_layout_changed (GtkLayoutManager *manager)
   if (priv->widget != NULL)
     gtk_widget_queue_resize (priv->widget);
 }
+
+/**
+ * gtk_layout_manager_get_layout_child:
+ * @manager: a #GtkLayoutManager
+ * @widget: a #GtkWidget
+ *
+ * Retrieves a #GtkLayoutChild instance for the #GtkLayoutManager, creating
+ * one if necessary
+ *
+ * The #GtkLayoutChild instance is owned by the #GtkLayoutManager, and is
+ * guaranteed to exist as long as @widget is a child of the #GtkWidget using
+ * the given #GtkLayoutManager.
+ *
+ * Returns: (transfer none): a #GtkLayoutChild
+ */
+GtkLayoutChild *
+gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
+                                     GtkWidget        *widget)
+{
+  GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
+  GtkLayoutChild *res;
+  GtkWidget *parent;
+
+  g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), NULL);
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  parent = gtk_widget_get_parent (widget);
+  g_return_val_if_fail (parent != NULL, NULL);
+
+  if (priv->widget != parent)
+    {
+      g_critical ("The parent %s %p of the widget %s %p does not "
+                  "use the given layout manager of type %s %p",
+                  gtk_widget_get_name (parent), parent,
+                  gtk_widget_get_name (widget), widget,
+                  G_OBJECT_TYPE_NAME (manager), manager);
+      return NULL;
+    }
+
+  if (GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child == NULL)
+    {
+      g_critical ("The layout manager of type %s %p does not create "
+                  "GtkLayoutChild instances",
+                  G_OBJECT_TYPE_NAME (manager), manager);
+      return NULL;
+    }
+
+  /* We store the LayoutChild into the Widget, so that the LayoutChild
+   * instance goes away once the Widget goes away
+   */
+  res = g_object_get_qdata (G_OBJECT (widget), quark_layout_child);
+  if (res != NULL)
+    {
+      /* If the LayoutChild instance is stale, and refers to another
+       * layout manager, then we simply ask the LayoutManager to
+       * replace it, as it means the layout manager for the parent
+       * widget was replaced
+       */
+      if (gtk_layout_child_get_layout_manager (res) == manager)
+        return res;
+    }
+
+  res = GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child (manager, widget);
+  g_assert (res != NULL);
+  g_assert (g_type_is_a (G_OBJECT_TYPE (res), GTK_TYPE_LAYOUT_CHILD));
+
+  g_object_set_qdata_full (G_OBJECT (widget), quark_layout_child,
+                           res,
+                           g_object_unref);
+
+  return res;
+}
index 4d05132c4dc01479388f681fd46e0324d71de04b..02f2173a36eacbfec1150f9d17fc428700f858b3 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <gtk/gtktypes.h>
 #include <gtk/gtkwidget.h>
+#include <gtk/gtklayoutchild.h>
 
 G_BEGIN_DECLS
 
@@ -48,23 +49,26 @@ struct _GtkLayoutManagerClass
   GObjectClass parent_class;
 
   /*< public >*/
-  GtkSizeRequestMode (* get_request_mode) (GtkLayoutManager *manager,
-                                           GtkWidget        *widget);
+  GtkSizeRequestMode (* get_request_mode)    (GtkLayoutManager *manager,
+                                              GtkWidget        *widget);
 
-  void               (* measure)          (GtkLayoutManager *manager,
-                                           GtkWidget        *widget,
-                                           GtkOrientation    orientation,
-                                           int               for_size,
-                                           int              *minimum,
-                                           int              *natural,
-                                           int              *minimum_baseline,
-                                           int              *natural_baseline);
+  void               (* measure)             (GtkLayoutManager *manager,
+                                              GtkWidget        *widget,
+                                              GtkOrientation    orientation,
+                                              int               for_size,
+                                              int              *minimum,
+                                              int              *natural,
+                                              int              *minimum_baseline,
+                                              int              *natural_baseline);
 
-  void               (* allocate)         (GtkLayoutManager *manager,
-                                           GtkWidget        *widget,
-                                           int               width,
-                                           int               height,
-                                           int               baseline);
+  void               (* allocate)            (GtkLayoutManager *manager,
+                                              GtkWidget        *widget,
+                                              int               width,
+                                              int               height,
+                                              int               baseline);
+
+  GtkLayoutChild *   (* create_layout_child) (GtkLayoutManager *manager,
+                                              GtkWidget        *widget);
 
   /*< private >*/
   gpointer _padding[16];
@@ -95,4 +99,8 @@ GtkWidget *             gtk_layout_manager_get_widget           (GtkLayoutManage
 GDK_AVAILABLE_IN_ALL
 void                    gtk_layout_manager_layout_changed       (GtkLayoutManager *manager);
 
+GDK_AVAILABLE_IN_ALL
+GtkLayoutChild *        gtk_layout_manager_get_layout_child     (GtkLayoutManager *manager,
+                                                                 GtkWidget        *widget);
+
 G_END_DECLS
index ce3147e5afed7e5e473ef7498981d13d35cfef10..88f78c4f4a9a51b44a39b2a176e42d6cd95e2f04 100644 (file)
@@ -256,6 +256,7 @@ gtk_public_sources = files([
   'gtkinfobar.c',
   'gtklabel.c',
   'gtklayout.c',
+  'gtklayoutchild.c',
   'gtklayoutmanager.c',
   'gtklevelbar.c',
   'gtklinkbutton.c',
@@ -507,6 +508,7 @@ gtk_public_headers = files([
   'gtkinfobar.h',
   'gtklabel.h',
   'gtklayout.h',
+  'gtklayoutchild.h',
   'gtklayoutmanager.h',
   'gtklevelbar.h',
   'gtklinkbutton.h',